#!/usr/bin/env python

"""
 Pyjon parsed tree into JooScript syntax converter.

 It actually converts special calls into JooScript syntax, "var" into "let",
 and replacing namespaces.

 Author: Evgen Burzak <buzzilo@gmail.com>
"""

import sys
import node2js, joo_node2js, defs

NodeToJS = node2js.NodeToJS

class JSNodeToJoo(NodeToJS):
    """
    JSNodeToJoo takes JS node and converts it into Joo syntax
    """
    def __init__(self, parent): 
        NodeToJS.__init__(self, parent)
        self.unit_obj = getattr(parent, "unit_obj", None)

    restArg = "__rest__"

    def convert_unit(self, n, i, c):
        self.check(n,subnodes=2)
        args = n[1]
        assert len(args) == 3, "unexpected length of unit arguments"
        assert args[2].type == "FUNCTION", "expected function"
        path_ = self.o(args[0], i, c)
        file_ = self.o(args[1], i, c)
        unit = args[2]
        body = unit.body
        unit_obj = root_obj = glob_obj = None

        if len(unit.params) >= 1:
            unit_obj = unit.params[0]
            if unit_obj != joo_node2js.JooNodeToJS.unit_obj:
                defs.err("WARNING: unit object name is differ from " + \
                            joo_node2js.JooNodeToJS.unit_obj)

        if len(unit.params) >= 2:
            root_obj = unit.params[1]
            if root_obj != joo_node2js.JooNodeToJS.root_obj:
                defs.err("WARNING: root object name is differ from " + \
                            joo_node2js.JooNodeToJS.root_obj)

        if len(unit.params) == 3:
            glob_obj = unit.params[2]
            if glob_obj != joo_node2js.JooNodeToJS.glob_obj:
                defs.err("WARNING: glob object name is differ from " + \
                            joo_node2js.JooNodeToJS.glob_obj)

        ctx = UnitContext(self, path_, file_, unit_obj, root_obj, glob_obj, body)
        return ctx.convert_unit(i,c)

    def convert_package(self, n, i, c):
        self.check(n,subnodes=2)
        context = n[0]
        # Is unit.Package(...) or $Package(...)?
        if (isinstance(self, UnitContext) and len(context) == 2 and \
                context[0].value in (self.unit_obj, "this")) or \
                context.type == "IDENTIFIER":
            assert n[1][1].type == "FUNCTION", "expected function"
            pack = n[1][1]
            assert n[1][0].type == "STRING", "expected string"
            group = n[1][0].value
            group_obj = None
            if len(pack.params)>0:
                group_obj = pack.params[0]
                if group_obj != joo_node2js.JooNodeToJS.group_obj:
                    defs.err("WARNING: group object name is differ from " + 
                                    joo_node2js.JooNodeToJS.group_obj)

            ctx = PackageContext(self, group, group_obj, pack.body)
            return ctx.convert_package(i,c)
        
        else:
            return self.convert_function_call(n,i,c)

    def convert_class(self, n, i, c):
        context = n[0]
        # Is unit.buildClass(...) or $buildClass?
        if (len(context) == 2 and context[0].value == self.unit_obj) or \
                context.type == "IDENTIFIER":
            self.check(n,subnodes=2)
            ctx = ClassContext(self, n[1])
            return ctx.convert_class(i, c)

        else:
            return self.convert_function_call(n,i,c)

    def convert_function(self, n, i, c):
        #self.check(n,subnodes=2)
        ctx = FunctionContext(self)
        return ctx.convert_function(n,i,c)

    def convert_import(self, n, i, c):
        context = n[0]
        args = n[1]
        if isinstance(self, UnitContext) and len(context) == 2 and \
                context[0].value in (self.unit_obj, "this"):
            self.check(n,subnodes=2)
            return "import %s" % ", ".join([x.value for x in args])

        else:
            return self.convert_function_call(n,i,c)

    def convert_and(self, n, i, c):
        self.check(n,subnodes=2)
        return "%s and %s" % (self.o(n[0],i,c), self.o(n[1],i,c))

    def convert_or(self, n, i, c):
        self.check(n,subnodes=2)
        return "%s or %s" % (self.o(n[0],i,c), self.o(n[1],i,c))

    def convert_not(self, n, i, c):
        self.check(n,subnodes=1)
        return "not %s" % (self.o(n[0],i,c))

    def convert_call(self, n, i, c):
        if n[0].type == "IDENTIFIER":
            method = n[0].value[0] == "$" and n[0].value[1:] or n[0].value
        elif n[0].type == "DOT":
            method = n[0][-1].value
        else:
            method = None

        #print method

        #if False: pass
        if method == "Unit":
            return self.convert_unit(n,i,c)

        elif method == "Package":
            return self.convert_package(n,i,c)

        elif method == "buildClass":
            return self.convert_class(n,i,c)

        elif method == "Import":
            return self.convert_import(n,i,c)

        else:
            return self.convert_function_call(n,i,c)

    def convert_function_call(self, n, i, c):
        return NodeToJS.convert_call(self,n,i,c)

# Unit context
class UnitContext(JSNodeToJoo): 
    def __init__(self, parent, path_, file_, \
                    unit_obj, root_obj, glob_obj, body):
        JSNodeToJoo.__init__(self, parent)
        self.path = path_
        self.file_ = file_
        self.unit_obj = unit_obj
        self.root_obj = root_obj
        self.glob_obj = glob_obj
        self.body = body

    def convert_unit(self, i, c):
        return self.o(self.body, i, c)

    def convert_script(self, n, i, c):
        return JSNodeToJoo.convert_script(self, n, i, c)

# Package context
class PackageContext(JSNodeToJoo):
    def __init__(self, parent, group, group_obj, body):
        JSNodeToJoo.__init__(self, parent)
        self.group = group
        self.group_obj = group_obj
        self.body = body

    def convert_package(self, i, c):
        return "package %s %s" % (self.group, self.o(self.body, i, c))

    def convert_script(self, n, i, c):
        return JSNodeToJoo.convert_script(self, n, i, c)

# Class context
class ClassContext(JSNodeToJoo):
    def __init__(self, parent, args):
        JSNodeToJoo.__init__(self, parent)

        self.ns = []
        self.className = ""
        self.role = {}
        self.ancestors = []
        self.classBody = None

        # class name
        if len(args) >= 1:
            assert args[0].type in ("STRING", "NULL"), "class name: expected string or null"
            if args[0].type == "STRING":
                self.className = args[0].value

        # ancestors
        if len(args) >= 2:
            assert args[1].type in ("ARRAY_INIT", "NULL"), "class ancestors: expected array or null"
            if args[1].type == "ARRAY_INIT" and len(args[1]) > 0:
                self.ancestors = args[1]

        self.class_obj = None

        # class body
        if len(args) >= 3:
            assert args[2].type in ("FUNCTION", "NULL"), "class body: expected function or null"
            if args[2].type == 'FUNCTION':
                if len(args[2].params) > 0:
                    self.class_obj = args[2].params[0]
                    if self.class_obj != joo_node2js.JooNodeToJS.class_obj:
                        defs.err("WARNING: class object name is differ from " + 
                                        joo_node2js.JooNodeToJS.class_obj)
                self.classBody = args[2].body

        # role
        if len(args) == 4:
            # TODO check that role is object, in any other case required
            # that node will be evaluated, but eval is not implemented at this moment
            assert args[3].type == "OBJECT_INIT", \
                        "class role: " + args[3].type + " not implemented"
            spec = args[3]
            for prop in spec:
                name = spec[0]
                fields = spec[1]
                assert fields.type == "OBJECT_INIT", "not implemented"
                for field in fields:
                    
                #PROPERTY_INIT

    def convert_class(self, i, c):
        className = self.className
        ancestors = ""
        classBody = ""
        ancestors = len(self.ancestors) and \
                        ", ".join([ self.o(x,i,c) for x in self.ancestors ]) or ""
        classBody = self.classBody and self.o(self.classBody,i,c) or "{}"
        role = "" 
        ns = ""
        return "%sclass %s%s(%s) %s" % (ns, className, role, ancestors, classBody)

    def convert_script(self, n, i, c):
        return JSNodeToJoo.convert_script(self, n, i, c)

# Function context 
class FunctionContext(JSNodeToJoo):
    def __init__(self, parent):
        JSNodeToJoo.__init__(self, parent)

    def convert_function(self, n, i, c):
        return NodeToJS.convert_function(self, n, i, c)

def convert(parsetree, include_props=False):
    ctx = JSNodeToJoo(None)
    return ctx.convert(parsetree, "", {"include_props": include_props}) + "\n"
